//! 交互监视器
//!
//! 交互监视器协议提供了 readline 和 json protocol (smp) 两种交互类型.
//!
//! readline 通过命令行方式与系统交互, 并且可以注册命令以及实现自动补全等功能.
//!
//! json protocol 通过 json 格式文本与系统交互, 并返回规范的 json 格式文本响应.
#![feature(extract_if)]

use rcu::{call_rcu, RcuLock};

mod command_impl;
use std::{collections::HashMap, io, path::PathBuf, sync::Arc};

pub use command_impl::*;

mod monitor_impl;
pub use monitor_impl::*;

mod protocol;
pub use protocol::{protocol_ok, Protocol};
use protocol::{MonitorProtocol, MonitorProtocolResult};

mod prompt;
pub use prompt::*;

mod readline;
pub use readline::MonitorSignal;
use readline::{parse_arg_type, parse_line, MonitorReadline, PrinterImpl};
use reedline::ExternalPrinter;

mod command;

/// 监视器管理
///
/// 提供 readline 以及 protocol 功能管理
pub struct Monitor {
    command_hash: Arc<RcuLock<HashMap<String, Arc<dyn MonitorCommand + Sync + Send>>>>,
    readline: MonitorReadline,
    printer: MonitorPrinter,
}

impl Monitor {
    pub(crate) fn commnad_hash_clone(
        &self,
    ) -> Arc<RcuLock<HashMap<String, Arc<dyn MonitorCommand + Sync + Send>>>> {
        self.command_hash.clone()
    }

    pub(crate) fn new(option: MonitorBuilder) -> Self {
        let command_hash = Arc::new(RcuLock::new(HashMap::new()));
        let printer = ExternalPrinter::default();
        let printer_clone = PrinterImpl { p: printer.clone() };
        let mon_printer = MonitorPrinter::create(Box::new(printer_clone));
        let readline = MonitorReadline::new(option, command_hash.clone(), printer);

        Self { command_hash, readline, printer: mon_printer }
    }

    // 初始化监视器命令
    pub(crate) fn init(&self) {
        self.register_readline_protocol_exec();
        self.register_help();
    }

    /// 注册一个监视器命令
    ///
    /// # Errors
    /// 当注册的命令已经存在时将返回错误
    #[allow(clippy::missing_panics_doc)]
    pub fn register_command(
        &self,
        command: &str,
        data: Arc<dyn MonitorCommand + Sync + Send>,
    ) -> MonResult<()> {
        let mut lock = self.command_hash.write().unwrap();
        if lock.contains_key(command) {
            return Err(MonError::ExistCommand);
        }
        lock.insert(command.to_string(), data);
        Ok(())
    }

    /// 卸载一个命令
    ///
    /// # Errors
    /// 当需要卸载的命令不存在时将返回错误
    #[allow(clippy::missing_panics_doc)]
    pub fn unregister_command(&self, command: &str) -> MonResult<()> {
        let mut lock = self.command_hash.write().unwrap();
        let result = lock.remove(command).ok_or(MonError::NoCommand)?;
        call_rcu(move || drop(result));
        Ok(())
    }

    /// 从标准输入读取一行
    ///
    /// 从标准输入读取一行, 此函数除非读取到一行, 或者收到信号否则不会返回
    ///
    /// 进入 readline 后, 提供 tab 补全, 信号捕捉
    ///
    /// # Errors
    /// 当读取时遇到系统错将返回错误
    pub fn readline(&self) -> io::Result<MonitorSignal> {
        self.readline.readline()
    }

    /// 将缓存的命令历史与本地文件同步
    ///
    /// # Errors
    /// 当遇到系统错误时将会返回错误
    pub fn sync_history(&self) -> io::Result<()> {
        self.readline.sync_history()
    }

    /// 打印历史命令信息
    ///
    /// # Errors
    /// 当遇到系统错误时将会返回错误
    pub fn print_history(&self) -> io::Result<()> {
        self.readline.print_history()
    }

    /// 清屏
    ///
    /// # Errors
    /// 当遇到系统错误时将会返回错误
    pub fn clear_scrollback(&self) -> io::Result<()> {
        self.readline.clear_scrollback()
    }

    pub(crate) fn readline_exec_get_hashmap(
        data: &Arc<dyn MonitorCommand + Send + Sync>,
        args: &[&str],
    ) -> MonResult<HashMap<String, String>> {
        let check_arg_type = data.mon_readline_arg_type();
        let mut args_map = HashMap::new();
        if let Some(check) = check_arg_type {
            args_map = parse_arg_type(check, args)?;
        } else {
            for (i, str) in args.iter().enumerate() {
                args_map.insert((i + 1).to_string(), (*str).to_string());
            }
        }
        Ok(args_map)
    }

    /// readline 执行一条命令
    ///
    /// # Errors
    /// 命令返回执行结果
    ///
    /// # Panics
    /// 命令可以自行 panic
    pub fn readline_exec(
        &self,
        line: &str,
        extra_printer: Option<&MonitorPrinter>,
    ) -> MonResult<()> {
        let res = parse_line(line, 0, false);

        let Some((command, args)) = res else {
            return Ok(());
        };
        let lock = self.command_hash.read().unwrap();
        let data = lock.get(command).ok_or(MonError::NoCommand)?;

        let args_map = Monitor::readline_exec_get_hashmap(data, &args)?;
        let printer = if let Some(p) = extra_printer { p } else { &self.printer };
        data.mon_readline_exec(&args_map, printer)
    }

    pub(crate) fn protocol_exec_raw(
        command_hash: Arc<RcuLock<HashMap<String, Arc<dyn MonitorCommand + Sync + Send>>>>,
        json: &str,
    ) -> String {
        let mp = MonitorProtocol::from_json(json).map_err(|_| MonError::InvalidProtocol);
        match mp {
            Ok(p) => {
                let command = p.command();
                let lock = command_hash.read().unwrap();
                let c = lock.get(command).ok_or(MonError::NoCommand);
                match c {
                    Ok(cc) => {
                        let res = cc.mon_protocol_exec(p.args());
                        MonitorProtocolResult::new(res).to_json()
                    },
                    Err(e) => MonitorProtocolResult::new(Err(e)).to_json(),
                }
            },
            Err(e) => MonitorProtocolResult::new(Err(e)).to_json(),
        }
    }

    /// protocol 执行一条 json 格式文本
    pub fn protocol_exec(&self, json: &str) -> String {
        Monitor::protocol_exec_raw(self.command_hash.clone(), json)
    }

    /// 向监视器输出信息
    pub fn print(&self, msg: String, extra_printer: Option<&MonitorPrinter>) {
        let printer = if let Some(p) = extra_printer { p } else { &self.printer };
        printer.print(msg);
    }
}

/// Monitor 监视器构建
#[derive(Default)]
pub struct MonitorBuilder {
    // 命令行是否着色
    pub(crate) use_color: bool,
    // 如果补全条目只有一个则快速补全
    pub(crate) quick_completions: bool,
    // 本地命令历史记录文件
    pub(crate) history: Option<PathBuf>,
    // 命令行提示器实现
    pub(crate) prompt: Option<MonitorPrompt>,
    // 根据历史记录暗示
    pub(crate) hinter: bool,
    // 启用 vi 编辑器模式
    pub(crate) vi: bool,
    // 启用 TAB 选中下一个备选元素
    pub(crate) tab_next: bool,
}

impl MonitorBuilder {
    /// Monitor 构建器
    pub fn builder() -> Self {
        Self::default()
    }

    /// 是否着色命令行
    pub fn use_color(mut self, color: bool) -> Self {
        self.use_color = color;
        self
    }

    /// 是否启动快速补全
    pub fn quick_completions(mut self, quick: bool) -> Self {
        self.quick_completions = quick;
        self
    }

    /// 本地命令历史记录文件
    pub fn history(mut self, history: PathBuf) -> Self {
        self.history = Some(history);
        self
    }

    /// 命令行提示
    ///
    /// 可以使用默认的 `DefaultMonitorPrompt` 实现
    pub fn prompt(mut self, prompt: MonitorPrompt) -> Self {
        self.prompt = Some(prompt);
        self
    }

    /// 历史记录暗示
    ///
    /// 根据历史记录暗示接下来可能输入的字符串
    pub fn hinter(mut self, hinter: bool) -> Self {
        self.hinter = hinter;
        self
    }

    /// 启用 vi 编辑器模式
    ///
    /// 启用 vi 编辑器模式将修改部分命令行交互样式和行为
    pub fn vi(mut self, vi: bool) -> Self {
        self.vi = vi;
        self
    }

    /// 启用 TAB 选中下一个备选元素
    ///
    /// 启用 `tab_next` 后, 当键入 TAB 进入补全菜单后, 再次使用 TAB 不再选中元素,
    /// 而是选择下一个备选元素
    ///
    /// 注意:
    ///
    /// 启用后, 当只有一个备选元素时, 不会自动补全该元素, 因为 TAB 此时的含义是选中下一个元素
    pub fn tab_next(mut self, tab_next: bool) -> Self {
        self.tab_next = tab_next;
        self
    }

    /// 构建 Monitor
    pub fn build(self) -> Monitor {
        let mon = Monitor::new(self);
        mon.init();
        mon
    }
}
